// ignore_for_file: non_constant_identifier_names

import 'dart:io';

import 'package:path/path.dart' as p;

import './cli_logger.dart';

extension StarkylinEnum on Enum {
  get label {
    return toString().split('.').last;
  }
}

enum PriorityType {
  optional, // 可选
  important, // 重要
  standard, // 标准
  _null,
  ;

  static PriorityType? getByString(String? str) {
    if (str is! String || str.isEmpty) return null;

    var ret = PriorityType.values.firstWhere(
      (element) => element.label == str,
      orElse: () => _null,
    );
    return ret != _null ? ret : null;
  }
}

// control 字段内容格式
class ControlContent {
  final Map<String, dynamic> controlObj;
  final List<String> requiredKeys = ['Package', 'Version', 'Description'];
  // 软件包名，只支持小写英文、数字以及“+”、“-”字符
  late String Package;
  // 程序版本，需要确保无连字符
  late String Version;
  // 该软件包的描述
  late String Description;

  // 软件所属类别
  String? Section;
  // 软件包优先级优先级
  PriorityType? Priority;
  // CPU 架构
  String? Architecture;
  // 必要的依赖关系
  List<String?>? Depends;
  // 大多数时候需要的依赖包
  List<String?>? Recommends;
  // 建议依赖包
  List<String?>? Suggests;
  // 维护者
  String? Maintainer;
  // 冲突软件包
  List<String?>? Conflicts;
  // 将被本包替换的软件包
  List<String?>? Replaces;
  // 是否为必需
  bool? Essential;
  // 备用的虚拟名称
  List<String?>? Provides;

  ControlContent(this.controlObj) {
    _handleControl(controlObj);
  }

  /// 处理控制对象，检查必需键并验证其值。
  ///
  /// 参数：
  ///   controlObj (Map<String, dynamic>): 要处理的控制对象。
  ///
  /// 返回：
  ///   void
  void _handleControl(Map<String, dynamic> controlObj) {
    var exitCode = 0;
    for (final key in requiredKeys) {
      var key1 = key.toLowerCase();
      if ((controlObj[key] == null || controlObj[key] == '') &&
          (controlObj[key1] == null || controlObj[key1] == '')) {
        // 缺少必要的 key，提醒用户添加
        CliLogger.error(
            "Your 'control' section does not contain a '$key' target.");
        exitCode = 1;
      }
    }

    if (exitCode != 0) {
      exit(exitCode);
    }

    try {
      Package = controlObj['Package'] ?? controlObj['package'];
      Version = controlObj['Version'] ?? controlObj['version'];
      Description = controlObj['Description'] ?? controlObj['description'];
      Section = controlObj['Section'] ?? controlObj['section'];
      Priority = PriorityType.getByString(
          controlObj['Priority'] ?? controlObj['priority']);
      Essential = controlObj['Essential'] ?? controlObj['essential'];
      Architecture = controlObj['Architecture'] ?? controlObj['architecture'];
      Depends =
          _handleStringOrList(controlObj['Depends'] ?? controlObj['depends']);
      Recommends = _handleStringOrList(
          controlObj['Recommends'] ?? controlObj['recommends']);
      Suggests =
          _handleStringOrList(controlObj['Suggests'] ?? controlObj['suggests']);
      Maintainer = controlObj['Maintainer'] ?? controlObj['maintainer'];
      Conflicts = _handleStringOrList(
          controlObj['Conflicts'] ?? controlObj['conflicts']);
      Replaces =
          _handleStringOrList(controlObj['Replaces'] ?? controlObj['replaces']);
      Essential = controlObj['Essential'] ?? controlObj['essential'];
      Provides =
          _handleStringOrList(controlObj['Provides'] ?? controlObj['provides']);
    } catch (e) {
      // 捕获错误
      CliLogger.error("Your 'control' section is not valid.");
      exit(1);
    }

    var packageRegex = RegExp(r'^[a-z0-9+]+(-[a-z0-9+]+)*$');
    if (!packageRegex.hasMatch(Package)) {
      CliLogger.error(
          "Your 'control' section does not contain a valid 'Package' target.\n Please make sure that the 'Package' target is a valid package name.\n The name must only contain lowercase letters, numbers, and the '+' and '-' characters.");
      exit(1);
    }
  }

  List<String?>? _handleStringOrList(dynamic dependencies) {
    // 处理依赖
    if (dependencies != null) {
      if (dependencies is String) {
        return dependencies.split(',');
      } else if (dependencies is List<String>) {
        return dependencies;
      }
    }

    return null;
  }

  String _getContentString() {
    final content = <String>[];
    content.add('Package: $Package');
    content.add('Version: $Version');
    content.add('Description: $Description');
    if (Priority != null) content.add('Priority: ${Priority!.label}');
    if (Section != null) content.add('Section: $Section');
    if (Architecture != null) content.add('Architecture: $Architecture');
    if (Depends != null) content.add('Depends: ${Depends!.join(', ')}');
    if (Provides != null) content.add('Provides: ${Provides!.join(', ')}');
    if (Recommends != null) {
      content.add('Recommends: ${Recommends!.join(', ')}');
    }
    if (Suggests != null) content.add('Suggests: ${Suggests!.join(', ')}');
    if (Conflicts != null) content.add('Conflicts: ${Conflicts!.join(', ')}');
    if (Replaces != null) content.add('Replaces: ${Replaces!.join(', ')}');
    if (Maintainer != null) content.add('Maintainer: $Maintainer');
    return content.join('\n');
  }

  void build(String buildDist) {
    Directory debianDir = Directory(p.join(buildDist, 'DEBIAN'));
    if (!debianDir.existsSync()) {
      debianDir.createSync(recursive: true);
    }

    File controlFile = File(p.join(debianDir.path, 'control'));
    controlFile.writeAsStringSync(_getContentString());
  }
}

class DirsContent {
  final Map<dynamic, dynamic> dirsObj;
  late Map<dynamic, dynamic> opt;

  DirsContent(this.dirsObj) {
    opt = _handleDirs(dirsObj);
  }

  Map<dynamic, dynamic> _handleDirs(Map<dynamic, dynamic> dirsObj) {
    var map = {};
    for (var element in dirsObj.entries) {
      if (element.value is Map) {
        // 有子级 Map，继续处理
        map[element.key] = _handleDirs(element.value as Map<dynamic, dynamic>);
      } else {
        if (element.value is List) {
          // 字符串列表
          map[element.key] = element.value;
        } else if (element.value is String) {
          // 处理地址 String
          map[element.key] = element.value;
        }
      }
    }
    return map;
  }

  Future<void> _copyDirectory(Directory sourceDir, Directory targetDir) async {
    // 获取源目录下的所有子项
    final entries = sourceDir.listSync();

    // 创建目标目录
    await targetDir.create(recursive: true);

    // 遍历源目录中的每一项
    for (var entry in entries) {
      if (entry is Directory) {
        // 如果是目录，则递归复制
        await _copyDirectory(entry,
            Directory('${targetDir.path}/${entry.path.split('/').last}'));
      } else if (entry is File) {
        // 如果是文件，则复制文件
        await entry.copy('${targetDir.path}/${entry.path.split('/').last}');
      }
    }
  }

  void _copy(String source, String targetDir) {
    if (source.endsWith('/')) {
      // 复制文件夹
      source = source.substring(0, source.length - 1);
      var src = Directory(source);
      if (!src.existsSync()) {
        CliLogger.error('The source directory `$source`/ does not exist.');
        exit(1);
      } else {
        var target = Directory('$targetDir/${src.path.split('/').last}');
        print('target $target, src $src');
        _copyDirectory(src, target);
      }
    } else {
      // 复制文件
      var src = File(source);
      if (!src.existsSync()) {
        CliLogger.error('The source file `$source` does not exist.');
        exit(1);
      } else {
        var target = File('$targetDir/${src.path.split('/').last}');

        src.copySync(target.path);
      }
    }
  }

  void build(String buildDist) {
    var dirMap = opt;
    print(opt);

    do {
      var recordMap = {};

      for (var entry in dirMap.entries) {
        // 检查是否存在当前遍历器所指向的目录是否存在，不存在则创建该目录
        var directory = Directory(p.join(buildDist, entry.key));
        if (!directory.existsSync()) {
          print("create ${directory.path}");
          directory.createSync(recursive: true);
        }

        if (entry.value is String && (entry.value as String).isNotEmpty) {
          // 如果是字符串，表明是文件/文件夹，进行复制
          _copy(entry.value, directory.path);
        } else if (entry.value is Map) {
          for (var subentry in (entry.value as Map).entries) {
            var folderPath = '${entry.key}/${subentry.key}';
            recordMap[folderPath] = subentry.value;
          }
        } else if (entry.value is List) {
          for (var element in entry.value) {
            if (element is String) {
              _copy(element, directory.path);
            } else if (element is Map) {
              for (var subentry in element.entries) {
                var folderPath = '${entry.key}/${subentry.key}';
                recordMap[folderPath] = element;
              }
            }
          }
        }
      }

      dirMap = recordMap;
    } while (dirMap.isNotEmpty);
  }
}
